#ifndef _CHILD_H
#define _CHILD_H

#include "stdio.h"
#include "base.h"
#include "reg.h"
#include "string.h"
#include "mem.h"

extern int nBank;

int checkValueValue(int, int);
int checkValueNotValue(Reg reg, int os, int value);
int checkNotValueNotValue(Reg reg1, int os1, Reg reg2, int os2);

//Load Word
class Lw:public Base
{
	int destReg, srcReg, os, baseAddr, data;
	Reg reg_s;
	int address;
	int conflict;
public:
	char* getMnemonic(){
		return "LW";
	}
	Lw(int _PC,int _destReg, int _srcReg, int _os):Base(_PC){
		destReg = _destReg;
		srcReg = _srcReg;
		os = _os;
		reg_s = RegFile::reg[srcReg];
		//read memory
		Mem::readMem(srcReg, os, RegFile::reg[destReg]);
	}
	bool isMem(){
		return true;
	}
	int checkConflict(Base* lastMemInst){
		Reg prevReg;
		int prevOffset;
		lastMemInst->getReg(prevReg);
		prevOffset = lastMemInst->getOffset();
		if (prevReg.isValue && reg_s.isValue){
			return checkValueValue(prevReg.value + prevOffset, reg_s.value + os);
		}
		else if (prevReg.isValue && !reg_s.isValue){
			return checkValueNotValue(reg_s, os, prevReg.value + prevOffset);
		}
		else if (!prevReg.isValue && reg_s.isValue){
			return checkValueNotValue(prevReg, lastMemInst->getOffset(), reg_s.value + os);
		}
		else {
			return checkNotValueNotValue(prevReg, lastMemInst->getOffset(), reg_s, os);
		}
	}
	void getReg(Reg& reg){
		reg = reg_s;
	}
	int getOffset(){
		return os;
	}
};

//Store Word
class Sw:public Base
{
	int destReg, srcReg, os, baseAddr, data;
	Reg reg_d;
	int address;
public:
	char* getMnemonic(){
		return "SW";
	}
	Sw(int _PC,int _destReg, int _srcReg, int _os):Base(_PC){
		destReg = _destReg;
		srcReg = _srcReg;
		os = _os;
		reg_d = RegFile::reg[destReg];
		//read memory
		Mem::writeMem(srcReg, os, RegFile::reg[destReg]);
	}
	bool isMem(){
		return true;
	}
	int checkConflict(Base* lastMemInst){
		Reg prevReg;
		int prevOffset;
		lastMemInst->getReg(prevReg);
		prevOffset = lastMemInst->getOffset();
		if (prevReg.isValue && reg_d.isValue){
			return checkValueValue(prevReg.value + prevOffset, reg_d.value + os);
		}
		else if (prevReg.isValue && !reg_d.isValue){
			return checkValueNotValue(reg_d, os, prevReg.value + prevOffset);
		}
		else if (!prevReg.isValue && reg_d.isValue){
			return checkValueNotValue(prevReg, lastMemInst->getOffset(), reg_d.value + os);
		}
		else {
			return checkNotValueNotValue(prevReg, lastMemInst->getOffset(), reg_d, os);
		}
	}
	void getReg(Reg& reg){
		reg = reg_d;
	}
	int getOffset(){
		return os;
	}
};

//Load Word Immediate
//v1
class Lwi:public Base
{
	int destReg, intValue;
public:
	char* getMnemonic()
	{
		return "LWI";
	}
	Lwi(int _PC,int _destReg, int _intValue):Base(_PC)
	{
		destReg = _destReg;
		intValue = _intValue;
		setReg();
	}
	void setReg(){
		RegFile::reg[destReg].isValue = true;
		RegFile::reg[destReg].value = intValue;
	}
};

//Add Integer
class Iadd:public Base
{
	int destReg, sReg1, sReg2;
	
public:
	char* getMnemonic()
	{
		return "IADD";
	}
	Iadd(int _PC,int _destReg, int _sReg1, int _sReg2):Base(_PC)	{
		destReg = _destReg;
        sReg1 = _sReg1;
        sReg2 = _sReg2;
		setReg();
	}
	void setReg(){
		if (RegFile::reg[sReg1].isValue && RegFile::reg[sReg2].isValue){
			RegFile::reg[destReg].isValue = true;
			RegFile::reg[destReg].value = RegFile::reg[sReg1].value + RegFile::reg[sReg2].value;
		}
		else if (RegFile::reg[sReg1].isValue && !RegFile::reg[sReg2].isValue){
			RegFile::reg[destReg].isValue = false;
			RegFile::reg[destReg].hash = RegFile::reg[sReg2].hash;
			RegFile::reg[destReg].delta = RegFile::reg[sReg2].delta + RegFile::reg[sReg1].value;
			RegFile::reg[destReg].coefficient = RegFile::reg[sReg2].coefficient;
		}
		else if (!RegFile::reg[sReg1].isValue && RegFile::reg[sReg2].isValue){
			RegFile::reg[destReg].isValue = false;
			RegFile::reg[destReg].hash = RegFile::reg[sReg1].hash;
			RegFile::reg[destReg].delta = RegFile::reg[sReg1].delta + RegFile::reg[sReg2].value;
			RegFile::reg[destReg].coefficient = RegFile::reg[sReg1].coefficient;
		}
		else if (RegFile::reg[sReg1].hash != RegFile::reg[sReg2].hash){
			RegFile::reg[destReg].init(++hashCount);
		}
		else {
			RegFile::reg[destReg].isValue = false;
			RegFile::reg[destReg].hash = RegFile::reg[sReg1].hash;
			RegFile::reg[destReg].delta = RegFile::reg[sReg1].delta + RegFile::reg[sReg2].delta;
			RegFile::reg[destReg].coefficient = RegFile::reg[sReg1].coefficient + RegFile::reg[sReg2].coefficient;
		}
	}
};

//Subtract Integer
class Isub:public Base
{
	int destReg, sReg1, sReg2;
	
public:
	char* getMnemonic()	{
		return "ISUB";
	}
	Isub(int _PC,int _destReg, int _sReg1, int _sReg2):Base(_PC)	{
		destReg = _destReg;
        sReg1 = _sReg1;
        sReg2 = _sReg2;
		setReg();
	}
	void setReg(){
		if (RegFile::reg[sReg1].isValue && RegFile::reg[sReg2].isValue){
			RegFile::reg[destReg].isValue = true;
			RegFile::reg[destReg].value = RegFile::reg[sReg1].value - RegFile::reg[sReg2].value;
		}
		else if (RegFile::reg[sReg1].isValue && !RegFile::reg[sReg2].isValue){
			RegFile::reg[destReg].isValue = false;
			RegFile::reg[destReg].hash = RegFile::reg[sReg2].hash;
			RegFile::reg[destReg].delta = RegFile::reg[sReg1].value - RegFile::reg[sReg2].delta;
			RegFile::reg[destReg].coefficient = - RegFile::reg[sReg2].coefficient;
		}
		else if (!RegFile::reg[sReg1].isValue && RegFile::reg[sReg2].isValue){
			RegFile::reg[destReg].isValue = false;
			RegFile::reg[destReg].hash = RegFile::reg[sReg1].hash;
			RegFile::reg[destReg].delta = RegFile::reg[sReg1].delta - RegFile::reg[sReg2].value;
			RegFile::reg[destReg].coefficient = RegFile::reg[sReg1].coefficient;
		}
		else if (RegFile::reg[sReg1].hash != RegFile::reg[sReg2].hash){
			RegFile::reg[destReg].init(++hashCount);
		}
		else {
			RegFile::reg[destReg].isValue = false;
			RegFile::reg[destReg].hash = RegFile::reg[sReg1].hash;
			RegFile::reg[destReg].delta = RegFile::reg[sReg1].delta - RegFile::reg[sReg2].delta;
			RegFile::reg[destReg].coefficient = RegFile::reg[sReg1].coefficient - RegFile::reg[sReg2].coefficient;
		}
	}
};

//Multiply Integer
class Imul:public Base
{
	int destReg, sReg1, sReg2;
	
public:
	char* getMnemonic(){
		return "IMUL";
	}
	Imul(int _PC,int _destReg, int _sReg1, int _sReg2):Base(_PC){
		destReg = _destReg;
        sReg1 = _sReg1;
        sReg2 = _sReg2;
		setReg();
	}
	void setReg(){
		if (RegFile::reg[sReg1].isValue && RegFile::reg[sReg2].isValue){
			RegFile::reg[destReg].isValue = true;
			RegFile::reg[destReg].value = RegFile::reg[sReg1].value * RegFile::reg[sReg2].value;
		}
		else if (RegFile::reg[sReg1].isValue && !RegFile::reg[sReg2].isValue){
			RegFile::reg[destReg].isValue = false;
			RegFile::reg[destReg].hash = RegFile::reg[sReg2].hash;
			RegFile::reg[destReg].delta = RegFile::reg[sReg1].value * RegFile::reg[sReg2].delta;
			RegFile::reg[destReg].coefficient =  RegFile::reg[sReg2].coefficient * RegFile::reg[sReg1].value;
		}
		else if (!RegFile::reg[sReg1].isValue && RegFile::reg[sReg2].isValue){
			RegFile::reg[destReg].isValue = false;
			RegFile::reg[destReg].hash = RegFile::reg[sReg1].hash;
			RegFile::reg[destReg].delta = RegFile::reg[sReg1].delta * RegFile::reg[sReg2].value;
			RegFile::reg[destReg].coefficient = RegFile::reg[sReg1].coefficient * RegFile::reg[sReg2].value;
		}
		else {
			RegFile::reg[destReg].init(++hashCount);
		}
	}
};

//Divide Integer
class Idiv:public Base
{
	int destReg, sReg1, sReg2;
	
public:
	char* getMnemonic()
	{
		return "IDIV";
	}
	Idiv(int _PC,int _destReg, int _sReg1, int _sReg2):Base(_PC)
	{
		destReg = _destReg;
        sReg1 = _sReg1;
        sReg2 = _sReg2;
		setReg();
	}
	void setReg(){
		if (RegFile::reg[sReg1].isValue && RegFile::reg[sReg2].isValue){
			RegFile::reg[destReg].isValue = true;
			RegFile::reg[destReg].value = RegFile::reg[sReg1].value / RegFile::reg[sReg2].value;
		}
		else {
			RegFile::reg[destReg].init(++hashCount);
		}
	}
};

//Modular Integer
class Imod:public Base
{
	int destReg, sReg1, sReg2;
	
public:
	char* getMnemonic()
	{
		return "IMOD";
	}
	Imod(int _PC,int _destReg, int _sReg1, int _sReg2):Base(_PC)
	{
		destReg = _destReg;
        sReg1 = _sReg1;
        sReg2 = _sReg2;
		setReg();
	}
	void setReg(){
		if (RegFile::reg[sReg1].isValue && RegFile::reg[sReg2].isValue){
			RegFile::reg[destReg].isValue = true;
			RegFile::reg[destReg].value = RegFile::reg[sReg1].value % RegFile::reg[sReg2].value;
		}
		else {
			RegFile::reg[destReg].init(++hashCount);
		}
	}
};
#endif
